var firelynx = function(){
    var _eventListeners = new Array();
    var _clientObjects = new Array();

    function _sortListeners(start, end,array) {
        if(start < end) {
            var splitter = _sortListenersSplit(start, end,array);
            _sortListeners(start, splitter - 1,array);
            _sortListeners(splitter + 1, end,array);
        }
    }
    function  _sortListenersSplit(start, end, array) {
        var i = start;
        var j = end -1;
        var pivot =  array[end].executionTime;
        while(i < j) {
            while(array[i].executionTime <=  pivot && i < end) {
                i++;
            }
            while(array[j].executionTime >=  pivot && j > start) {
                j--;
            }
            if(i < j) {
                firelynx.swapArray(i, j,array);
                j = j - 1;
                i = i + 1;
            }
        }
        if(array[i].executionTime > pivot) {
            array =_swapArray(i, end,array);
        }
        return i;

    }
    function _swapArray(a, b, array) {
        var temp = array[a];
        array[a] =  array[b];
        array[b] =  temp;
    }
    return {
        id : 'System',
        registerClientObject : function(clientObject){
            _clientObjects.push(clientObject);
        },
        findClientObject : function(clientObjectId){
            var clientObject = null;
            for(var i = 0; i <  _clientObjects.length; i++ ){
                if(_clientObjects[i].id ==  clientObjectId){
                    clientObject = _clientObjects[i];
                    break;
                }
            }
            return clientObject;
        },
        elementExists: function(element){
            var id = element.id;
            var findElement = document.getElementById(id);
            if(findElement!=null)
                return true;
            else
                return false;
        },
        unregisterClientObject : function(clientObject){
            for(var i = 0; i <  _clientObjects.length; i++ ){
                if(_clientObjects[i].id ==  clientObject.id){
                    _clientObjects.splice(i, 1);
                    break;
                }
            }
        },
        unregisterEventListener : function(sender, eventType){
            for(var i =0; i < _eventListeners.length;i++){
                if(_eventListeners[i].sender.id==sender.id&& (eventType == '' || _eventListeners[i].eventType==eventType)){
                    _eventListeners.splice(i, 1);
                    break;
                }
            }
        },
        cleanUp : function(){
            // walking backwards though object becaue maybe some clientObjects will be deleting while looping
            for(var i = _clientObjects.length -1; i >=  0; i-- ){
                _clientObjects[i].cleanUp();
            }

            for(var j = _eventListeners.length -1; j>=  0; j--){
                if(_eventListeners[j].sender==null){
                    _eventListeners.splice(j, 1);
                }
                else if(typeof(_eventListeners[j].sender.parentNode) !== 'undefined' && !firelynx.elementExists( _eventListeners[j].sender)) {
                    // is a node element but doesn't have a parent any more
                    _eventListeners.splice(j, 1);
                }
            }
        },
        eventListenerExits : function (sender, eventType){
            var exists = false;
            for(var i =0; i < _eventListeners.length;i++){
                if(_eventListeners[i].sender.id==sender.id&&_eventListeners[i].eventType==eventType){
                    exists = true;
                    break;
                }
            }
            return exists;
        },
        fireEvent : function (eventArgs){
            var listeners = new Array();
            for(var i =0; i < _eventListeners.length;i++){
                if(_eventListeners[i].sender.id==eventArgs.sender.id&&_eventListeners[i].eventType==eventArgs.eventType){
                    listeners.push(_eventListeners[i]);
                }
            }
            _sortListeners(0, listeners.length -1,listeners);
            for(var j =0; j < listeners.length;j++){
                var callBackFunc = listeners[j].callBackFunc;
                // call my function
                callBackFunc(eventArgs);
            }
        },
        pageLoad : function (callBackFunc){
            firelynx.listenToEvent(firelynx, 'pageLoad', 0,callBackFunc);
        },
        listenToEvent :function (sender, eventType,executionTime, callBackFunc){
                // if it is the first time the eventType is added to the sender
                if(firelynx.eventListenerExits(sender, eventType)==false){
         
                    // add event to sender
                    firelynx.addEvent(sender, eventType,
                        function (e)
                        {
                            if (!e) var e = window.event;
                            var eventArgs = firelynx.EventArgs(e,sender, eventType);
                            // fire event
                            firelynx.fireEvent(eventArgs);

                            if(eventArgs.cancelEvent==true){
                                if(eventArgs.event.preventDefault) eventArgs.event.preventDefault();
                                eventArgs.event.returnValue =false;
                            }
                        }
                        );
                }
            // append listener
            _eventListeners.push(firelynx.EventListener(sender, eventType, callBackFunc, executionTime));
        },
        addEvent: function (obj,evt,fn) {
            if (obj.addEventListener)
                obj.addEventListener(evt,fn,false);
            else if (obj.attachEvent)
                obj.attachEvent('on'+evt,fn);
        },

        removeEvent :function (obj,evt,fn) {
            if (obj.removeEventListener)
                obj.removeEventListener(evt,fn,false);
            else if (obj.detachEvent)
                obj.detachEvent('on'+evt,fn);
        },

        ClientObject : function(id){
            var clientObject = {
                id : id,
                cleanUp : function(){
                // overwrite my in sub classes
                }
            };
            firelynx.registerClientObject(clientObject);
            return clientObject;
        },
        ConfirmButton : function(_elementId,_message, _executionTime){
            var my = firelynx.ClientObject(_elementId + "_ConfirmButton");
            my.element= document.getElementById(_elementId);
            my.message = _message;
            my.confirm = function(eventArgs){
                var confirmed = false;
                if(eventArgs.cancelEvent!=true){
                    confirmed = confirm(my.message);
                    if(!confirmed){
                        eventArgs.cancelEvent = true;
                    }
                }
                return confirmed;
            };
            my.cleanUp = function(){
                if(!firelynx.elementExists(my.element)){
                    firelynx.unregisterClientObject(my);
                    firelynx.unregisterEventListener(my.element, '');
                }
            }
            firelynx.listenToEvent( my.element, 'click', _executionTime,
                function (eventArgs){
                    my.confirm(eventArgs);
                });
            return my;
        },
        SubmitButton : function(_elementId, _validationGoup, _executionTime){
            var my = firelynx.ClientObject(_elementId + '_SubmitButton');
            my.element =document.getElementById(_elementId);
            my.validationGroup = _validationGoup;
            // overwrite
            my.cleanUp = function(){
                if(!firelynx.elementExists(my.element)){
                    firelynx.unregisterClientObject(my);
                    // unregister all events listeners for my submit button
                    firelynx.unregisterEventListener(my.element, '');
                }
            };

            // add event listener
            firelynx.listenToEvent(my.element,'click',_executionTime, function(eventArgs){
                if( eventArgs.cancelEvent!=true){
                    var submitEventArgs = firelynx.EventArgs(eventArgs.event,firelynx, 'Submit');
                    // add validation group
                    submitEventArgs.validationGroup = my.validationGroup;
                    // fire event
                    firelynx.fireEvent(submitEventArgs);
                    if(submitEventArgs.cancelEvent == true){
                        eventArgs.cancelEvent = true;
                    }
                }
            });

            return my;
        },
        AjaxContainer : function (_elementId, _file ,_loadingHtml){
            var my = firelynx.ClientObject(_elementId + '_AjaxContainer');
            my.file = _file;
            my.element = document.getElementById(_elementId);
            my.loadingHtml = _loadingHtml;
            my.getQueryString = function(eventType, sender)
            {
                var querystring =  my.file;
                if(my.file.indexOf("?")== -1){
                    querystring +=  "?";
                }
                else {
                    querystring +=  "&";
                }

                querystring +=  "firelynx_ajaxContainerId=" + encodeURIComponent(_elementId) + "&firelynx_ajaxContainerRequestFile=" +  encodeURIComponent(my.file);
                querystring +=  "&firelynx_ajaxContainerTriggerControlValue=" + encodeURIComponent(sender.value) +  "&firelynx_ajaxContainerTriggerControlTag=" + encodeURIComponent(sender.tagName) +  "&firelynx_ajaxContainerTriggerEvent=" + encodeURIComponent(eventType);
               
                var i = 0;
                if(sender.attributes){
                    for(i = 0; i <sender.attributes.length; i++ ){
                        if(sender.attributes[i].value!=null&&sender.attributes[i].value!=''&&sender.attributes[i].value != 'null'){
                            querystring = querystring +  "&firelynx_ajaxContainerTriggerControl["+ sender.attributes[i].name +"]=" + encodeURIComponent(sender.attributes[i].value);
                        }
                    }
                }

              
                var inputs = my.element.getElementsByTagName('input');
                for (i = 0; i < inputs.length; i++) {
                    querystring =querystring + "&" + inputs[i].name + "=" + encodeURIComponent(inputs[i].value);
                }
                var selects =  my.element.getElementsByTagName('select');
                for (i = 0; i < selects.length; i++) {
                    querystring =querystring + "&" + selects[i].name + "=" +encodeURIComponent(selects[i].value);
                }
                var textareas =  my.element.getElementsByTagName('textarea');
                for (i = 0; i < textareas.length; i++) {
                    querystring =querystring + "&" + textareas[i].name + "=" + encodeURIComponent(textareas[i].value);
                }
                return querystring;
            };
            my.ajaxRequest = function (eventArgs){
                var http = null;
                if (window.XMLHttpRequest) {
                    http = new XMLHttpRequest();
                } else if (window.ActiveXObject) {
                    http = new ActiveXObject("Microsoft.XMLHTTP");
                }
                if (http != null) {
                    // fire event
                    var ajaxContainerEventArgs = firelynx.EventArgs(eventArgs.event,my,'startAjaxRequest');
                    firelynx.fireEvent(ajaxContainerEventArgs);
                    if(my.loadingHtml!=null){
                        my.element.innerHTML = my.loadingHtml;
                    }
                    http.open("GET", my.getQueryString(eventArgs.eventType, eventArgs.sender), true);
                    http.onreadystatechange = function(){
                        if (http.readyState == 4)
                        {
                            var responseText =  http.responseText;
                            var response= responseText.split('~-#-#-#-~firelynx_AjaxContainerResponse~-#-#-#-~');

                            // clean html
                            while(my.element.hasChildNodes()){
                                my.element.removeChild(my.element.lastChild);
                            }
                            // clean the client objects
                            firelynx.cleanUp();
                             // update html
                           
                           try {
                                 my.element.innerHTML = response[1];
                          }
                          catch (e) {
                              if( my.element.nodeName=="TR"){
                                    var html = response[1]
                                    // find the first td tag
                                    var posTagStart =  html.indexOf("<td");
                                    var posTagEnd = 0;
                                    var posEnd;
                                    var tagHTML;
                                    var innerHTML;
                                    while(posTagStart !==-1){
                                            // search the end of the beginning tag
                                            posTagEnd = html.indexOf(">", posTagStart);
                                            // search the end of the td
                                            posEnd = html.indexOf("</td>", posTagEnd);
                                            // get the tag html
                                            tagHTML = html.substring(posTagStart, posTagEnd + 1);
                                            // get the inner html
                                            innerHTML = html.substring(posTagEnd + 1, posEnd);
                                            // create td and automatically set attributes (works only in IE)
                                            var td  = document.createElement(tagHTML);
                                            // set inner html
                                            td.innerHTML = innerHTML
                                            // append td to tr
                                            my.element.appendChild(td);
                                            // search next beginning tag
                                            posTagStart = html.indexOf("<td", posEnd);
                                    }
                              }
                          }
                            //my.element.innerHTML = response[1];

                            // remove old javascript code and add new
                            var script = document.getElementById("firelynx_ajaxRequestGeneratedJavascript");
                            if(script!=null){
                                script.parentNode.removeChild(script);
                            }
                            script   = document.createElement('script');
                            script.id = "firelynx_ajaxRequestGeneratedJavascript";
                            script.setAttribute("type", "text/javascript")
                            script.text = response[0];
                            document.getElementsByTagName('head')[0].appendChild(script);
                            // fire event
                            ajaxContainerEventArgs.eventType = 'finishedAjaxRequest';
                            firelynx.fireEvent(ajaxContainerEventArgs);
                        }
                    };
                    http.send(null);
                    // cancel event
                    eventArgs.cancelEvent = true;
                }
            };
            my.triggerElements = new Array();
            my.addTrigger = function(elementId, triggerEvent, excutionTime){
                // listen to event
                var triggerElement = document.getElementById(elementId);
                firelynx.listenToEvent(triggerElement, triggerEvent, excutionTime, function(eventArgs){
                    if(eventArgs.cancelEvent==false){
                        my.ajaxRequest(eventArgs);
                    }
                });
                // add to array
                my.triggerElements.push(document.getElementById(elementId));
            }
            my.cleanUp = function(){
                // clean up triggers
                for(var i =  my.triggerElements.length -1 ; i > -1; i--){
                    if(!firelynx.elementExists(my.triggerElements[i])){
                        firelynx.unregisterEventListener(my.triggerElements[i], '');
                        my.triggerElements.splice(i, 1);
                    }
                }
                if(!firelynx.elementExists(my.element)){
                    firelynx.unregisterClientObject(my);
                }
            }
            return my;
        },
        Validator : function (_clientObjectId, _controlId, _controlToValidateId, _validationGroup, _displayStyle, _executionTime){
            var my = firelynx.ClientObject(_clientObjectId);
            my.control = document.getElementById(_controlId);
            my.controlToValidate =  document.getElementById(_controlToValidateId);
            my.validationGroup = _validationGroup;
            my.displayStyle = _displayStyle;
            my.check = function(){
                return false;
            };
            // overwrite
            my.cleanUp= function(){
                if(!firelynx.elementExists(my.control)||!firelynx.elementExists(my.controlToValidate)){
                    firelynx.unregisterClientObject(my);
                }
            };
            // listen to the system event 'submit'
            firelynx.listenToEvent(firelynx,'Submit',_executionTime, function(eventArgs){
                if(eventArgs.validationGroup == '' ||  eventArgs.validationGroup == my.validationGroup){
                    var isValid = my.check();
                    if(isValid==false){
                        eventArgs.cancelEvent=true;
                    }
                }
            });
            return my;
        },
        RequiredFieldValidator : function (_controlId, _controlToValidateId, _validationGroup, _displayStyle, _executionTime){
            var my = firelynx.Validator(_controlToValidateId + '_RequiredFieldValidator' , _controlId, _controlToValidateId, _validationGroup, _displayStyle, _executionTime);
            my.check = function()
            {
                var isValid = false;
                if(my.control!=null){
                    if(my.controlToValidate != null && my.control != null){
                        if(my.controlToValidate.value != ""){
                            isValid = true;
                            my.control.style.display = "none";
                        }
                        else {
                            my.control.style.display = my.displayStyle;
                        }
                    }
                }
                return isValid;
            };
            return my;

        },

        RegularExpressionValidator : function (_controlId, _controlToValidateId, _validationGroup, _displayStyle ,_executionTime, _regularExpression){
            var my = firelynx.Validator(_controlToValidateId + '_RegularExpressionValidator',_controlId, _controlToValidateId, _validationGroup, _displayStyle,_executionTime );
            my.regularExpression = _regularExpression;
            my.check = function()
            {
                var isValid = false;
                if(my.control!=null&&my.controlToValidate!=null){
                    var reg = new RegExp(my.regularExpression);
                    if (reg.test(my.controlToValidate.value)){
                        isValid = true;
                        my.control.style.display = "none";
                    }
                    else {
                        my.control.style.display = my.displayStyle;
                    }
                }
                return isValid;
            };
            return my;
        },
        CompareValidator : function(_controlId, _controlToValidateId, _validationGroup, _displayStyle , _executionTime ,  _controlToValidate2Id){
            var my = firelynx.Validator(_controlToValidateId + '_CompareValidator',_controlId, _controlToValidateId, _validationGroup, _displayStyle, _executionTime);
            my.controlToValidate2 = document.getElementById(_controlToValidate2Id);
            my.check =  function()
            {
                var isValid = false;
                if(my.control!=null&&my.controlToValidate!=null&&my.controlToValidate2!=null){
                    if (my.controlToValidate.value == my.controlToValidate2.value){
                        isValid = true;
                        my.control.style.display = "none";
                    }
                    else {
                        my.control.style.display = my.displayStyle;
                    }
                }
                return isValid;

            };
            return my;
        },
        PopupPanel : function(_elementId){
            var my = firelynx.ClientObject(_elementId + '_PopupPanel');
            my.element = document.getElementById(_elementId);
            my.overlay = null;
            my.isOpen = false;
            my.open = function(){
                my.overlay  = document.createElement('div');
                my.overlay.id = _elementId + "_overlay";
                my.overlay.style.width =document.documentElement.clientWidth  + "px";
                my.overlay.style.height = document.documentElement.clientHeight + "px";
                document.body.appendChild(my.overlay);
                my.element.style.display  = "block";
                 var x  = my.overlay.clientWidth/2-my.element.clientWidth/2;
                 var y  = my.overlay.clientHeight/2-my.element.clientHeight/2;
                my.element.style.position = 'absolute';
                my.element.style.top = y + "px";
                my.element.style.left = x + "px";
                my.isOpen = true;
            };
            my.close = function(){
                my.element.style.display = 'none';
                if(my.overlay!=null&& my.overlay.parentNode!=null)
                    my.overlay.parentNode.removeChild(my.overlay);
                my.isOpen = false;
            };
            my.openTriggerElements = new Array();
            my.addOpenTrigger = function(_elementId, _triggerEvent, _executionZone){
                var element = document.getElementById(_elementId);
                firelynx.listenToEvent(element, _triggerEvent, _executionZone, function(eventArgs){
                    if(eventArgs.cancelEvent==false){
                        my.open();
                    }
                });
                my.openTriggerElements.push(element);
            };
            my.closeTriggerElements = new Array();
            my.addCloseTrigger = function(_elementId, _triggerEvent, _executionZone){
                var element = document.getElementById(_elementId);
                firelynx.listenToEvent(element, _triggerEvent, _executionZone, function(eventArgs){
                    if(eventArgs.cancelEvent==false){
                        my.close();
                    }
                });
                my.closeTriggerElements.push(element);
            };
            my.cancelTriggerElements = new Array();
            my.addCancelTrigger = function(_elementId, _triggerEvent, _executionZone){
                var element = document.getElementById(_elementId);
                firelynx.listenToEvent(element, _triggerEvent, _executionZone, function(eventArgs){
                    // always do it, don't check eventArgs.cancelEvent
                    my.close();
                });
                my.cancelTriggerElements.push(element);
            };
            my.cleanUp = function(){
                // clean up triggers
                var i = 0;
                for( i =  my.openTriggerElements.length -1 ; i > -1; i--){
                    if( my.openTriggerElements[i].parentNode==null){
                        firelynx.unregisterEventListener(my.openTriggerElements[i], '');
                        my.openTriggerElements.splice(i, 1);
                    }
                }
                for(i =  my.closeTriggerElements.length -1 ; i > -1; i--){
                    if( my.closeTriggerElements[i].parentNode==null){
                        firelynx.unregisterEventListener(my.closeTriggerElements[i], '');
                        my.closeTriggerElements.splice(i, 1);
                    }
                }
                for(i =  my.cancelTriggerElements.length -1 ; i > -1; i--){
                    if( my.cancelTriggerElements[i].parentNode==null){
                        firelynx.cancelTriggerElements(my.openTriggerElements[i], '');
                        my.cancelTriggerElements.splice(i, 1);
                    }
                }

                if(my.element.parentNode==null){
                    firelynx.unregisterClientObject(my);
                }
            }
           firelynx.listenToEvent(window, 'resize', 0, function(eventArgs){
                    // poup is open
                   if(my.isOpen==true){
                            // resize
                            my.overlay.style.width =document.documentElement.clientWidth  + "px";
                             my.overlay.style.height = document.documentElement.clientHeight + "px";
                             var x  = my.overlay.clientWidth/2-my.element.clientWidth/2;
                            var y  = my.overlay.clientHeight/2-my.element.clientHeight/2;
                            my.element.style.top = y + "px";
                            my.element.style.left = x + "px";
                   }
             });

            return my;
        },
        EventListener : function(sender, eventType, callBackFunc, executionTime){
            return {
                sender  : sender,
                eventType : eventType,
                callBackFunc : callBackFunc,
                executionTime : executionTime
            };
        },
        EventArgs : function(event,sender, eventType){
            return {
                event : event,
                eventType : eventType,
                cancelEvent: false,
                sender : sender
            };
        }
    };
}();